Skip to content

Conversation

@andikatuluspangestu
Copy link

@andikatuluspangestu andikatuluspangestu commented Dec 10, 2025

🎯 Overview

This PR adds full Linux and macOS support to CLIProxyAPIPlus-Easy-Installation, making it a truly cross-platform installation toolkit.

✨ What's New

🐧 Linux/macOS Installation Scripts

  • install-cliproxyapi.sh (389 lines) - Main installer with OS/architecture auto-detection
  • start-cliproxyapi.sh (207 lines) - Server lifecycle manager with background/foreground modes
  • cliproxyapi-oauth.sh (252 lines) - Interactive OAuth login for 8 providers
  • update-cliproxyapi.sh (234 lines) - Update from prebuilt binaries or source
  • uninstall-cliproxyapi.sh (164 lines) - Clean removal with optional auth preservation
  • gui-cliproxyapi.sh (203 lines) - GUI launcher with browser auto-open

🖥️ GUI Control Center for Linux

  • cliproxyapi-api-server.py (309 lines) - Python REST API backend for GUI management
  • Updated gui/index.html (603 lines added) - Enhanced with API integration
  • Implements 12+ REST endpoints: /api/status, /api/start, /api/stop, /api/oauth/{provider}, etc.
  • Fixes "Unexpected token '<'" error by providing proper JSON responses

📚 Documentation

  • Updated README.md and README_ID.md with Linux/macOS installation sections
  • Added testing instructions (server status, models list, chat completion, logs)
  • Enhanced scripts/AGENTS.md with bash patterns and conventions (+177 lines)

🔧 Technical Details

Installation Features

  • Auto-detects OS (Linux/Darwin) and architecture (amd64/arm64)
  • Downloads prebuilt binaries from GitHub releases or builds from source
  • Handles both binary names: cli-proxy-api-plus and cliproxyapi-plus
  • Installs to ~/.local/bin with proper PATH configuration
  • Copies GUI files to ~/.local/share/cliproxyapi/gui/

Server Management

  • PID-based process tracking (stores in ~/.cli-proxy-api/server.pid)
  • Background mode with nohup and log rotation
  • Graceful shutdown with fallback to force kill
  • Status monitoring with endpoint health checks

GUI Features

  • Python API server on port 8318 for GUI backend
  • Subprocess management for bash script execution
  • JSON responses for all API calls (no more HTML errors)
  • Auto-launches browser with xdg-open or open command

🧪 Testing

Tested on Ubuntu 22.04:

# Installation
[install-cliproxyapi.sh](http://_vscodecontentref_/0) --use-prebuilt --skip-oauth

# Server lifecycle
start-cliproxyapi --background
start-cliproxyapi --status
start-cliproxyapi --logs
start-cliproxyapi --stop

# OAuth (tested with Gemini and Antigravity)
cliproxyapi-oauth --gemini
cliproxyapi-oauth --all

# API endpoints
curl http://localhost:8317/v1/models
curl http://localhost:8317/v1/chat/completions -d '{"model":"gemini-2.0-flash-exp","messages":[{"role":"user","content":"test"}]}'

# GUI
gui-cliproxyapi  # Opens browser at http://localhost:8318

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds Linux and macOS support with a one-click installer, server manager, OAuth helper, update/uninstall tools, and a browser-based Control Center backed by a Python API. Makes the toolkit cross-platform and easier to install and run.

- **New Features**
  - Bash scripts for install, start/stop, OAuth, update, uninstall, and GUI.
  - Auto OS/arch detection, prebuilt or source builds, PATH setup to ~/.local/bin; GUI installed to ~/.local/share/cliproxyapi/gui.
  - Python API server on port 8318 powering the GUI with JSON endpoints (status/start/stop/restart/oauth/models).
  - Docs updated with Linux/macOS Quick Start and testing; AGENTS.md adds bash patterns and conventions.

- **Bug Fixes**
  - GUI now returns proper JSON to avoid “Unexpected token '<'”.
  - Release installer detects both cliproxyapi-plus and cli-proxy-api-plus binary names.

<sup>Written for commit 85acfe7dae2ab7cc2c314b37fae6548ee74e38b5. Summary will update automatically on new commits.</sup>

<!-- End of auto-generated description by cubic. -->



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

## Release Notes

* **New Features**
  * Installation script with prebuilt binary and source build options
  * GUI Control Center with REST API for server management
  * OAuth login flows for multiple AI providers
  * Server manager supporting start, stop, restart, and status operations
  * Update and uninstall maintenance scripts

* **Documentation**
  * Expanded Quick Start guides for Linux, macOS, and Windows
  * Enhanced configuration examples for multiple CLI tools and AI providers
  * Portuguese language documentation support

<sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

- Add 6 bash scripts for full Linux/macOS support
  - install-cliproxyapi.sh: Auto-detect OS/arch, install binary
  - start-cliproxyapi.sh: Server manager with PID tracking
  - cliproxyapi-oauth.sh: Interactive OAuth login
  - update-cliproxyapi.sh: Update from source or prebuilt
  - uninstall-cliproxyapi.sh: Clean removal
  - gui-cliproxyapi.sh: Web-based control panel
- Update README.md and README_ID.md with Linux installation guide
- Add testing instructions for verification
- Update scripts/AGENTS.md with bash patterns and conventions
The official release uses 'cli-proxy-api-plus' as binary name,
not 'cliproxyapi-plus'. Updated find command to check both names.
Signed-off-by: andikatuluspangestu <andhikatuluspangestu@gmail.com>
- Install GUI files to ~/.local/share/cliproxyapi/gui/
- Update gui-cliproxyapi script to use GUI_DIR instead of GUI_HTML
- Auto-detect installed GUI location
- Fallback to temporary minimal GUI if not found
Signed-off-by: andikatuluspangestu <andhikatuluspangestu@gmail.com>
Signed-off-by: andikatuluspangestu <andhikatuluspangestu@gmail.com>
Signed-off-by: andikatuluspangestu <andhikatuluspangestu@gmail.com>
Copilot AI review requested due to automatic review settings December 10, 2025 09:50
@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

Walkthrough

This pull request introduces comprehensive installation, update, and server management infrastructure through new Bash and Python scripts, alongside extensive documentation updates. The changes include an HTTP API server for GUI control, interactive OAuth CLI, GUI launcher, one-click installer, server manager, uninstall and update utilities, and expanded README documentation with quick-start guides and configuration examples.

Changes

Cohort / File(s) Summary
Documentation & Guides
README.md, README_ID.md, scripts/AGENTS.md
Added Quick Start sections, installation flows, configuration guidance for multiple CLI tools, expanded model/provider tables, GUI features, usage examples, and Bash/PowerShell script documentation.
Installation & Setup
scripts/install-cliproxyapi.sh
New one-click installation script supporting prebuilt binary and source builds, OS/architecture detection, prerequisite validation, config and factory file generation, PATH setup, helper script installation, and optional OAuth prompts.
Server Management
scripts/start-cliproxyapi.sh, scripts/uninstall-cliproxyapi.sh, scripts/update-cliproxyapi.sh
New scripts for starting/stopping/restarting server with logging and PID tracking, uninstalling with optional config/token removal, and updating binaries from GitHub releases or source with version comparison and rollback capability.
OAuth & Authentication
scripts/cliproxyapi-oauth.sh
New interactive Bash CLI for OAuth login flows supporting 8 providers (Gemini, Antigravity, Copilot, Codex, Claude, Qwen, iFlow, Kiro) with per-provider wrappers, menu-driven interface, and CLI argument parsing.
GUI Infrastructure
scripts/gui-cliproxyapi.sh, scripts/cliproxyapi-api-server.py
New Bash GUI launcher with port validation, auto-browser launch, and embedded HTML fallback; new Python HTTP API server with endpoints for status, start/stop/restart, OAuth, config, models, stats, update checks, and CORS support.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant GUI as GUI Launcher<br/>(gui-cliproxyapi.sh)
    participant Server as API Server<br/>(cliproxyapi-api-server.py)
    participant Binary as CLIProxyAPI<br/>Binary
    participant FS as Filesystem<br/>(config/logs)

    User->>GUI: Run gui-cliproxyapi.sh [--port]
    activate GUI
    GUI->>GUI: Detect/generate GUI assets
    GUI->>GUI: Validate port availability
    GUI->>Server: Start API server (Python)
    activate Server
    Server->>FS: Read config.yaml
    Server->>FS: Scan auth status
    deactivate Server
    GUI->>User: Open browser → http://localhost:port
    deactivate GUI
    
    User->>GUI: Click "Start Server" (via browser)
    activate GUI
    GUI->>Server: GET /api/start
    activate Server
    Server->>Binary: Execute start-cliproxyapi binary
    activate Binary
    Binary->>FS: Create PID file, write logs
    Binary-->>Server: stdout (running state, endpoint)
    deactivate Binary
    Server->>FS: Parse output
    Server->>User: JSON {status, pid, endpoint}
    deactivate Server
    deactivate GUI
    
    User->>Server: GET /api/status
    activate Server
    Server->>Binary: Query process status
    Server->>User: JSON {running, pid, endpoint}
    deactivate Server
Loading
sequenceDiagram
    participant User
    participant Installer as install-cliproxyapi.sh
    participant GH as GitHub API
    participant FS as Filesystem
    participant Binary as Binary/Config

    User->>Installer: Run install-cliproxyapi.sh [--use-prebuilt]
    activate Installer
    Installer->>Installer: Detect OS & architecture
    Installer->>Installer: Validate prerequisites (git, curl, Go)
    
    alt Prebuilt Mode
        Installer->>GH: Fetch latest release metadata
        activate GH
        GH-->>Installer: JSON {tag_name, assets[]}
        deactivate GH
        Installer->>GH: Download binary asset (OS/ARCH)
        Installer->>FS: Extract & verify binary
    else Source Build Mode
        Installer->>FS: Clone or update repo
        Installer->>Installer: Run go build
        Installer->>FS: Place binary in target
    end
    
    Installer->>FS: Create config dirs (~/.cli-proxy-api, ~/.factory)
    Installer->>FS: Generate config.yaml & config.json
    Installer->>FS: Install binary to ~/.local/bin/cliproxyapi-plus
    Installer->>FS: Copy helper scripts (oauth, start, update, uninstall, gui)
    Installer->>FS: Copy Python API server script
    Installer->>FS: Update shell RC with PATH entry
    Installer->>User: Display installation summary & quick-start commands
    deactivate Installer
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas requiring extra attention:

  • scripts/install-cliproxyapi.sh: Error handling for missing prerequisites, binary extraction/verification, PATH modification edge cases, and config file generation logic
  • scripts/update-cliproxyapi.sh: Version comparison logic, GitHub API interaction, backup and restoration on failure, graceful server shutdown and restart sequencing
  • scripts/cliproxyapi-api-server.py: CORS policy, security of background subprocess execution (OAuth, update, start/stop commands), error responses, and file I/O on config/auth scanning
  • scripts/start-cliproxyapi.sh: PID file management and stale process cleanup, cross-platform process checking via ps
  • Cross-platform concerns: Path handling (\ vs /), binary detection, and compatibility across Linux/macOS/Windows for all new scripts

Poem

🐰 A warren of scripts, so clever and keen,
Installation and OAuth flows—so spry and pristine!
The GUI dances with API delight,
While updates and servers spin smoothly through night.
Behold! CLIProxyAPI, now managed with flair,
From setup to shutdown, it floats on the air! 🚀

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.28% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add Support Installation for Linux or Mac' is grammatically awkward but refers to a real, significant part of the changeset—adding installation support for Linux/macOS. However, it understates the broader scope of the PR, which includes lifecycle management scripts, OAuth, updates, uninstall, and a GUI backend. While technically related, the title focuses narrowly on installation rather than the main comprehensive additions.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (10)
scripts/cliproxyapi-oauth.sh (4)

1-37: Binary/config path checks are sound; consider executable check for robustness

Using $HOME/.local/bin/cliproxyapi-plus and $HOME/.cli-proxy-api/config.yaml is consistent and the early‑exit checks avoid confusing downstream failures. If you want a bit more robustness, you could also assert that the binary is executable ([ -x "$BINARY_PATH" ]) rather than just present, but that’s optional for local installs.


38-86: DRY opportunity across provider‑specific login functions

All login_* functions follow the same pattern (log message → invoke binary with --config and a flag → success message). You could use a small helper like login_provider "Gemini" "--login" to reduce duplication and keep messages/flags in one place. This will make adding/removing providers less error‑prone, but the current code is functionally fine.


115-152: Interactive menu UX is straightforward; optional improvement on invalid choice

The menu is clear and maps 1–9 directly to providers/all, with 0 to exit. On invalid input you currently print an error and exit. If you want a smoother UX, you could loop until a valid choice is entered instead of exiting, but this is not required.


181-252: Argument parsing supports multiple providers; minor behavior notes

The argument loop correctly allows combining flags (e.g. --gemini --copilot) and tracks whether any login was requested via LOGIN_ANY. A couple of small notes:

  • LOGIN_ANY is treated as a string flag ("true"), which works but could also be written as a shell boolean idiom (LOGIN_ANY=0/1) if you prefer.
  • When using --all, you’ll see both “All OAuth logins complete!” (from login_all) and the final “OAuth login process complete!” banner — acceptable but a bit redundant.

Functionally this is solid.

scripts/cliproxyapi-api-server.py (6)

61-142: Subprocess‑based server control is reasonable; consider narrowing exceptions and surfacing non‑200 states

The handle_status, handle_start, handle_stop, and handle_restart methods all use subprocess.run with explicit argument lists (no shell=True), timeouts, and capture_output=True, which is good from a safety perspective and aligns with the static‑analysis concern about untrusted input (here the commands are fully under your control, so risk is low).

A couple of improvements you might want to consider:

  • Instead of except Exception as e: everywhere, catch the specific failures you expect (e.g., FileNotFoundError, subprocess.TimeoutExpired) and optionally log unexpected exceptions. This would address the BLE001 hints and make debugging easier.
  • send_json always responds with HTTP 200, even on clear failures (e.g., subprocess non‑zero or exceptions). If you start integrating with other tooling beyond your own GUI, you may want to allow passing an HTTP status code (e.g. 5xx on internal errors) to distinguish success from failure at the protocol level.

Functionally this works, so these are more about robustness and observability.


246-261: Stats and update‑check stubs are fine; hard‑coded version should be kept in sync

The stubbed /api/stats and /api/update/check endpoints are fine for now and help stabilize the GUI. When you start wiring real data, consider:

  • Avoid hard‑coding "6.5.64" in two fields; instead, centralize it (env var, config, or module‑level constant) so it doesn’t get out of sync with the actual CLI version.
  • Returning "available": False is OK as long as the GUI clearly communicates that updates are not yet implemented.

No immediate issues.


263-279: Background update apply is straightforward

Spawning update-cliproxyapi in the background via subprocess.Popen without shell=True is fine, and returning a confirmation JSON immediately matches the intended UX. Just be sure that the update script logs somewhere visible, since this endpoint won’t surface errors that occur after the process has started.


280-287: send_json centralizes JSON output but always returns HTTP 200

Centralizing JSON response headers and CORS handling in send_json is good. The only downside is that you can’t currently distinguish success from failure at the HTTP level (everything is 200). If you later need richer error semantics, you might extend send_json to accept an optional status parameter and pass through non‑200 codes where appropriate.


288-291: Suppressing default logging reduces noise but may hide issues

Overriding log_message to pass keeps the console clean, which is nice for end‑users. For debugging, you may eventually want a simple verbosity flag or environment check that conditionally enables logging instead of disabling it entirely.


226-245: Consider using urllib.request instead of curl subprocess for model discovery

The current implementation using subprocess.run() to call curl works correctly with proper error handling and JSON parsing fallback. However, since curl is only used here (not elsewhere in the codebase), switching to urllib.request or http.client would eliminate the external runtime dependency and simplify the code without any functional loss. This is a straightforward refactoring opportunity if you want to reduce deployment requirements.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3ac55fd and 85acfe7.

📒 Files selected for processing (10)
  • README.md (9 hunks)
  • README_ID.md (8 hunks)
  • scripts/AGENTS.md (7 hunks)
  • scripts/cliproxyapi-api-server.py (1 hunks)
  • scripts/cliproxyapi-oauth.sh (1 hunks)
  • scripts/gui-cliproxyapi.sh (1 hunks)
  • scripts/install-cliproxyapi.sh (1 hunks)
  • scripts/start-cliproxyapi.sh (1 hunks)
  • scripts/uninstall-cliproxyapi.sh (1 hunks)
  • scripts/update-cliproxyapi.sh (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
scripts/uninstall-cliproxyapi.sh (5)
scripts/install-cliproxyapi.sh (3)
  • echo_warning (48-48)
  • echo_step (46-46)
  • echo_success (47-47)
scripts/gui-cliproxyapi.sh (3)
  • echo_warning (59-59)
  • echo_info (57-57)
  • echo_success (58-58)
scripts/update-cliproxyapi.sh (3)
  • echo_warning (37-37)
  • echo_step (35-35)
  • echo_success (36-36)
scripts/cliproxyapi-oauth.sh (3)
  • echo_warning (22-22)
  • echo_info (20-20)
  • echo_success (21-21)
scripts/start-cliproxyapi.sh (3)
  • echo_warning (23-23)
  • echo_info (21-21)
  • echo_success (22-22)
scripts/cliproxyapi-api-server.py (1)
scripts/start-cliproxyapi.sh (1)
  • is_running (27-38)
🪛 Ruff (0.14.8)
scripts/cliproxyapi-api-server.py

64-64: subprocess call: check for execution of untrusted input

(S603)


86-86: Do not catch blind exception: Exception

(BLE001)


92-92: subprocess call: check for execution of untrusted input

(S603)


104-104: Do not catch blind exception: Exception

(BLE001)


110-110: subprocess call: check for execution of untrusted input

(S603)


122-122: Do not catch blind exception: Exception

(BLE001)


128-128: subprocess call: check for execution of untrusted input

(S603)


140-140: Do not catch blind exception: Exception

(BLE001)


163-163: subprocess call: check for execution of untrusted input

(S603)


173-173: Do not catch blind exception: Exception

(BLE001)


185-185: Do not catch blind exception: Exception

(BLE001)


223-223: Do not catch blind exception: Exception

(BLE001)


223-223: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


230-231: Starting a process with a partial executable path

(S607)


243-243: Do not catch blind exception: Exception

(BLE001)


267-267: subprocess call: check for execution of untrusted input

(S603)


277-277: Do not catch blind exception: Exception

(BLE001)

🪛 Shellcheck (0.11.0)
scripts/update-cliproxyapi.sh

[warning] 9-9: REPO_URL appears unused. Verify use (or export if used externally).

(SC2034)

scripts/gui-cliproxyapi.sh

[warning] 54-54: MAGENTA appears unused. Verify use (or export if used externally).

(SC2034)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Agent
🔇 Additional comments (20)
README_ID.md (1)

120-160: Quick Start (Linux/macOS) section is well-documented.

The section provides clear installation paths, prerequisites, and post-installation steps aligned with the actual script implementations. Instructions for reloading shell config and using --use-prebuilt flag match the installer script behavior.

README.md (1)

120-168: Linux/macOS Quick Start documentation is comprehensive.

Provides clear one-click setup path and step-by-step post-installation instructions. Documentation aligns with actual script capabilities (--use-prebuilt, --background, etc.).

scripts/uninstall-cliproxyapi.sh (3)

1-46: Solid script header and initialization.

Proper shebang, argument parsing, error handling setup, and colored output functions. Structure follows best practices for Bash uninstall scripts.


80-123: File removal logic is well-structured and safe.

Proper existence checks, conditional removal based on flags, and sensible defaults (preserving auth tokens unless --all is used). Follows uninstall best practices.


125-164: Thoughtful PATH cleanup guidance and final status reporting.

Script appropriately detects shell configuration files and guides users to manually remove PATH entries, reducing risk of accidental corruption. Final summary clearly indicates what was/wasn't removed.

scripts/update-cliproxyapi.sh (1)

1-70: Update script header and initialization look solid.

Good OS/ARCH detection matching install script, version checking, and proper prerequisites validation. Architecture handling is consistent with rest of codebase.

scripts/start-cliproxyapi.sh (2)

1-38: Well-structured server manager with clear function organization.

Proper initialization, color definitions, and output functions. Script sets up all necessary paths and variables for server lifecycle management.


26-107: Solid process management with appropriate signal handling.

is_running() properly validates processes, start_server() correctly uses nohup for background execution, and stop_server() implements sensible graceful-shutdown-then-force pattern. Error handling at each step is appropriate.

scripts/install-cliproxyapi.sh (6)

1-70: Excellent installer structure with comprehensive system detection.

Proper header, variable initialization, argument parsing, and OS/ARCH detection matching other scripts. Foundation for the installation process is solid.


162-184: Source build path assumes specific project structure.

Line 179 runs go build -o ... ./cmd/server without verifying this path exists in the cloned repository. If CLIProxyAPIPlus uses a different build path, this will fail with unclear error messaging.

Consider adding a validation check:

if [ ! -f "$CLONE_DIR/cmd/server/main.go" ]; then
    echo_error "Expected build path ./cmd/server not found in cloned repo"
    exit 1
fi

However, this may be acceptable if CLIProxyAPIPlus' structure is stable. Document the assumption in a comment if intentional.


186-241: Config generation is well-structured with sensible defaults.

Both config.yaml and .factory/config.json are properly generated with appropriate defaults. Droid models list (18 entries) comprehensively covers all supported providers. JSON formatting is valid.


253-275: Smart PATH configuration with duplicate prevention.

Shell RC detection is robust (bash/zsh/profile fallback), duplicate checks prevent multiple PATH entries, and the script informs users when a shell restart is needed. Follows best practices for shell configuration.


277-306: Helper script installation is thoughtful but could verify source existence.

The script copies helper scripts with proper executable permissions and removes .sh extension for clean command names. However, it silently skips missing scripts (line 282 checks if [ -f "$SCRIPT_DIR/$script" ]). Consider whether silent skipping is acceptable or if missing scripts should trigger a warning.


46-61: Missing echo_info() function definition.

Line 61 calls echo_info() but the script only defines echo_step(), echo_success(), echo_warning(), and echo_error() at lines 46-49.

Add the missing function:

 echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; }
 echo_success() { echo -e "${GREEN}[+] $1${NC}"; }
 echo_warning() { echo -e "${YELLOW}[!] $1${NC}"; }
 echo_error() { echo -e "${RED}[-] $1${NC}"; }
+echo_info() { echo -e "${CYAN}[*] $1${NC}"; }

Likely an incorrect or invalid review comment.

scripts/AGENTS.md (1)

35-38: The review comment is based on incorrect information and does not apply to this repository.

The repository contains only PowerShell scripts (.ps1), not Bash scripts (.sh). AGENTS.md is a 154-line PowerShell documentation file with no Bash section. The referenced scripts gui-cliproxyapi.sh and install-cliproxyapi.sh do not exist; the actual scripts are gui-cliproxyapi.ps1 and install-cliproxyapi.ps1. The functions mentioned (echo_info(), echo_step()) are Bash naming conventions that do not appear in this codebase. The actual scripts use PowerShell's Write-Step, Write-Success, Write-Warning, and Write-Error functions, which are correctly documented in AGENTS.md lines 49-52. There are no non-existent lines 178-190 to update, and no inconsistency exists between documentation and implementation.

Likely an incorrect or invalid review comment.

scripts/cliproxyapi-oauth.sh (2)

87-113: login_all flow is clear and respects failures without aborting

The combined login flow with a confirmation prompt and per‑provider error handling (${FUNCS[$i]} || echo_warning ...) works well, especially combined with set -e (which is correctly suppressed by the || guard here). This looks good and should behave as users expect when some providers fail.


154-179: Help text matches implemented options

The show_help output correctly documents all supported flags and examples, including multi‑provider usage. Keeping this in sync with the argument parser will be important as you add/remove providers, but it’s consistent right now.

scripts/cliproxyapi-api-server.py (3)

20-60: Routing structure is clear and maintainable

The separation of GET and POST routes with dedicated handler methods keeps the surface area understandable. Using /api/... and delegating everything else to super().do_GET() for static GUI files is a clean pattern and should be easy to extend as you add new endpoints.


143-175: OAuth endpoint mapping is safe but depends on stable provider names

The /api/oauth/{provider} handler uses a fixed provider_map and only invokes cliproxyapi-oauth when the provider is recognized, so the S603 static warning about untrusted subprocess input is effectively mitigated here. Just be aware that the external URL schema and GUI must stay aligned with these keys (gemini, antigravity, etc.), or you’ll hit the “Unknown provider” path.

No further changes required from a correctness or security angle in this method.


176-187: Config endpoint is simple and robust for missing files

Reading config.yaml from CONFIG_DIR and returning an empty string when it doesn’t exist is straightforward. Wrapping file access in try/except and returning an error field on exception is a good pattern for the GUI to consume.

Comment on lines +188 to +225
def handle_auth_status(self):
"""Get auth status for providers"""
try:
auth_files = list(CONFIG_DIR.glob("*.json"))
providers = {
"gemini": False,
"antigravity": False,
"copilot": False,
"codex": False,
"claude": False,
"qwen": False,
"iflow": False,
"kiro": False
}

for f in auth_files:
name = f.stem.lower()
if "gemini" in name or "@gmail.com" in name:
providers["gemini"] = True
if "antigravity" in name:
providers["antigravity"] = True
if "copilot" in name or "github" in name:
providers["copilot"] = True
if "codex" in name:
providers["codex"] = True
if "claude" in name:
providers["claude"] = True
if "qwen" in name:
providers["qwen"] = True
if "iflow" in name:
providers["iflow"] = True
if "kiro" in name:
providers["kiro"] = True

self.send_json(providers)
except Exception as e:
self.send_json({p: False for p in ["gemini", "antigravity", "copilot", "codex", "claude", "qwen", "iflow", "kiro"]})

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Auth status detection works; fix unused exception variable and consider future‑proofing naming heuristics

The heuristic over *.json files in CONFIG_DIR to infer provider auth state is a practical solution and should work fine with your current naming conventions.

Two small issues:

  • In the except block you use except Exception as e: but never reference e. This is what Ruff flagged (F841). Either drop the as e or log the exception somewhere.
  • The string‑matching heuristics ("gemini" in name or "@gmail.com" in name, "copilot" in name or "github" in name, etc.) are tightly coupled to current filenames. If you change how auth artifacts are named, it would be easy to drift. Not urgent, but eventually a small metadata file listing authenticated providers would be more robust.

Proposed minimal fix for the unused variable:

-        except Exception as e:
-            self.send_json({p: False for p in ["gemini", "antigravity", "copilot", "codex", "claude", "qwen", "iflow", "kiro"]})
+        except Exception:
+            self.send_json({p: False for p in ["gemini", "antigravity", "copilot", "codex", "claude", "qwen", "iflow", "kiro"]})
🧰 Tools
🪛 Ruff (0.14.8)

223-223: Do not catch blind exception: Exception

(BLE001)


223-223: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

🤖 Prompt for AI Agents
In scripts/cliproxyapi-api-server.py around lines 188 to 225, the except block
declares an unused exception variable ("except Exception as e:") which triggers
a linter warning; change it to "except Exception:" (or log the exception via
process logger) and keep the fallback JSON response as-is; optionally consider
replacing the filename-heuristic with a small metadata file for provider auth
detection in future to avoid brittle name matching.

Comment on lines +292 to +307
def main():
"""Start the API server"""
# Change to GUI directory
gui_dir = HOME / ".local" / "share" / "cliproxyapi" / "gui"
if gui_dir.exists():
os.chdir(gui_dir)

with socketserver.TCPServer(("", PORT), APIHandler) as httpd:
print(f"API Server running on http://localhost:{PORT}")
print(f"GUI available at http://localhost:{PORT}")
print("Press Ctrl+C to stop")
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\nShutting down...")

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Bind server to localhost only to avoid exposing control API on the network

Right now you instantiate the server as:

with socketserver.TCPServer(("", PORT), APIHandler) as httpd:

Passing "" binds to all interfaces (0.0.0.0), which exposes this management API — including start/stop, OAuth triggering, config access, and update commands — on any reachable network interface. Given this is a local control plane for a desktop tool, that’s a security risk and unnecessary.

You almost certainly want to bind only to the loopback interface so it’s only accessible from the local machine:

-    with socketserver.TCPServer(("", PORT), APIHandler) as httpd:
+    with socketserver.TCPServer(("127.0.0.1", PORT), APIHandler) as httpd:

This still works seamlessly with the bundled GUI (which talks to localhost) but prevents other machines on the network from hitting these endpoints directly.

🤖 Prompt for AI Agents
In scripts/cliproxyapi-api-server.py around lines 292 to 307, the server is
currently bound to all interfaces via socketserver.TCPServer(("", PORT),
APIHandler) which exposes the control API on the network; change the bind
address to the loopback (e.g., "127.0.0.1" or "localhost") so the TCPServer
listens only on localhost (socketserver.TCPServer(("127.0.0.1", PORT),
APIHandler)), keeping the GUI functional while preventing remote access.

Comment on lines +49 to +61
# Colors
GREEN='\033[0;32m'
CYAN='\033[0;36m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
MAGENTA='\033[0;35m'
NC='\033[0m'

echo_info() { echo -e "${CYAN}[*] $1${NC}"; }
echo_success() { echo -e "${GREEN}[+] $1${NC}"; }
echo_warning() { echo -e "${YELLOW}[!] $1${NC}"; }
echo_error() { echo -e "${RED}[-] $1${NC}"; }

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unused color variable MAGENTA.

The MAGENTA variable is defined at line 54 but never used in the script. The color initialization includes MAGENTA but only GREEN, CYAN, YELLOW, RED, and NC are actually used.

Either remove the unused variable or apply it to the banner output for consistency with other scripts.

🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 54-54: MAGENTA appears unused. Verify use (or export if used externally).

(SC2034)

🤖 Prompt for AI Agents
In scripts/gui-cliproxyapi.sh around lines 49 to 61, the MAGENTA color variable
is defined but never used; remove the unused MAGENTA assignment or apply it to
an appropriate output (for example the banner) to keep color definitions
consistent. If removing, delete the MAGENTA line; if applying, change one echo
that should stand out (e.g., banner or header) to use ${MAGENTA}...${NC} and
ensure the echo function or literal uses -e so escape codes render correctly.

Comment on lines +185 to +202
# Start server with API backend
API_SERVER="$HOME/.local/bin/cliproxyapi-api-server"
if command -v python3 &> /dev/null; then
if [ -f "$API_SERVER" ]; then
# Use Python API server with management endpoints
python3 "$API_SERVER" 2>/dev/null
else
# Fallback to simple HTTP server
cd "$GUI_DIR"
python3 -m http.server "$GUI_PORT" 2>/dev/null
fi
elif command -v python &> /dev/null; then
cd "$GUI_DIR"
python -m SimpleHTTPServer "$GUI_PORT" 2>/dev/null
else
echo_error "No HTTP server available (python/python3 required)"
echo_info "Please install Python to use the GUI"
exit 1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

HTTP server startup logic could fail silently on Python API server.

The script attempts to start the API server (line 190) without verifying it's executable or properly handling stderr. If python3 "$API_SERVER" fails to start or exits, the fallback HTTP server won't start because the first command terminates the script execution (due to set -e at line 7).

Apply this fix to ensure fallback works:

 API_SERVER="$HOME/.local/bin/cliproxyapi-api-server"
 if command -v python3 &> /dev/null; then
     if [ -f "$API_SERVER" ]; then
-        # Use Python API server with management endpoints
-        python3 "$API_SERVER" 2>/dev/null
+        # Use Python API server with management endpoints
+        python3 "$API_SERVER" 2>/dev/null || cd "$GUI_DIR" && python3 -m http.server "$GUI_PORT" 2>/dev/null
     else
         # Fallback to simple HTTP server
         cd "$GUI_DIR"
         python3 -m http.server "$GUI_PORT" 2>/dev/null
     fi
 elif command -v python &> /dev/null; then
     cd "$GUI_DIR"
     python -m SimpleHTTPServer "$GUI_PORT" 2>/dev/null
 else
     echo_error "No HTTP server available (python/python3 required)"
     echo_info "Please install Python to use the GUI"
     exit 1
 fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Start server with API backend
API_SERVER="$HOME/.local/bin/cliproxyapi-api-server"
if command -v python3 &> /dev/null; then
if [ -f "$API_SERVER" ]; then
# Use Python API server with management endpoints
python3 "$API_SERVER" 2>/dev/null
else
# Fallback to simple HTTP server
cd "$GUI_DIR"
python3 -m http.server "$GUI_PORT" 2>/dev/null
fi
elif command -v python &> /dev/null; then
cd "$GUI_DIR"
python -m SimpleHTTPServer "$GUI_PORT" 2>/dev/null
else
echo_error "No HTTP server available (python/python3 required)"
echo_info "Please install Python to use the GUI"
exit 1
# Start server with API backend
API_SERVER="$HOME/.local/bin/cliproxyapi-api-server"
if command -v python3 &> /dev/null; then
if [ -f "$API_SERVER" ]; then
# Use Python API server with management endpoints
python3 "$API_SERVER" 2>/dev/null || cd "$GUI_DIR" && python3 -m http.server "$GUI_PORT" 2>/dev/null
else
# Fallback to simple HTTP server
cd "$GUI_DIR"
python3 -m http.server "$GUI_PORT" 2>/dev/null
fi
elif command -v python &> /dev/null; then
cd "$GUI_DIR"
python -m SimpleHTTPServer "$GUI_PORT" 2>/dev/null
else
echo_error "No HTTP server available (python/python3 required)"
echo_info "Please install Python to use the GUI"
exit 1

Comment on lines +48 to +78
# Confirmation prompt
if [ "$FORCE" = false ]; then
echo_warning "This will remove CLIProxyAPI-Plus from your system."
if [ "$REMOVE_ALL" = true ]; then
echo_warning "Including auth tokens and configuration files!"
else
echo_info "Auth tokens will be preserved (use --all to remove them)"
fi
echo ""
read -p "Continue? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo_info "Uninstall cancelled"
exit 0
fi
fi

# Stop server if running
PID_FILE="$CONFIG_DIR/server.pid"
if [ -f "$PID_FILE" ]; then
PID=$(cat "$PID_FILE")
if ps -p "$PID" > /dev/null 2>&1; then
echo_step "Stopping server..."
kill "$PID" 2>/dev/null || true
sleep 1
if ps -p "$PID" > /dev/null 2>&1; then
kill -9 "$PID" 2>/dev/null || true
fi
echo_success "Server stopped"
fi
fi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Undefined function reference in confirmation prompt.

Line 54 calls echo_info() but this function is not defined in the script. The script defines echo_step(), echo_success(), echo_warning(), and echo_error() at lines 35-38, but not echo_info().

Apply this fix:

 echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; }
 echo_success() { echo -e "${GREEN}[+] $1${NC}"; }
 echo_warning() { echo -e "${YELLOW}[!] $1${NC}"; }
 echo_error() { echo -e "${RED}[-] $1${NC}"; }
+echo_info() { echo -e "${CYAN}[*] $1${NC}"; }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In scripts/uninstall-cliproxyapi.sh around lines 48 to 78, the script calls the
undefined function echo_info at two places (the preservation message and
"Uninstall cancelled"); replace those echo_info calls with the existing
echo_step function (or implement a short echo_info wrapper that delegates to
echo_step) so the informational messages use a defined logger and the script no
longer references an undefined function.

Comment on lines +9 to +16
REPO_URL="https://github.com/router-for-me/CLIProxyAPIPlus.git"
RELEASE_API="https://api.github.com/repos/router-for-me/CLIProxyAPIPlus/releases/latest"
CLONE_DIR="$HOME/CLIProxyAPIPlus"
BIN_DIR="$HOME/.local/bin"
BINARY_NAME="cliproxyapi-plus"

USE_PREBUILT=false
FORCE=false
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Unused REPO_URL variable.

Line 9 defines REPO_URL="https://github.com/..." but it's never used in the script. The update logic uses RELEASE_API (line 10) for prebuilt downloads, or pulls from existing CLONE_DIR (line 11) for source updates.

Apply this fix to remove dead code:

-REPO_URL="https://github.com/router-for-me/CLIProxyAPIPlus.git"
 RELEASE_API="https://api.github.com/repos/router-for-me/CLIProxyAPIPlus/releases/latest"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
REPO_URL="https://github.com/router-for-me/CLIProxyAPIPlus.git"
RELEASE_API="https://api.github.com/repos/router-for-me/CLIProxyAPIPlus/releases/latest"
CLONE_DIR="$HOME/CLIProxyAPIPlus"
BIN_DIR="$HOME/.local/bin"
BINARY_NAME="cliproxyapi-plus"
USE_PREBUILT=false
FORCE=false
RELEASE_API="https://api.github.com/repos/router-for-me/CLIProxyAPIPlus/releases/latest"
CLONE_DIR="$HOME/CLIProxyAPIPlus"
BIN_DIR="$HOME/.local/bin"
BINARY_NAME="cliproxyapi-plus"
USE_PREBUILT=false
FORCE=false
🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 9-9: REPO_URL appears unused. Verify use (or export if used externally).

(SC2034)

🤖 Prompt for AI Agents
In scripts/update-cliproxyapi.sh around lines 9 to 16, the REPO_URL variable is
defined but never used; remove the unused REPO_URL declaration and any
references to it (if present elsewhere) so the script only defines and uses
RELEASE_API, CLONE_DIR, BIN_DIR, BINARY_NAME, USE_PREBUILT, and FORCE; run a
quick grep to confirm REPO_URL is not referenced elsewhere and update comments
or docs if they mention the removed variable.

Comment on lines +58 to +70
# Check if currently installed
if [ ! -f "$BIN_DIR/$BINARY_NAME" ]; then
echo_error "CLIProxyAPI-Plus is not installed"
echo_info "Run install-cliproxyapi first"
exit 1
fi

# Get current version
CURRENT_VERSION="unknown"
if [ -f "$BIN_DIR/$BINARY_NAME" ]; then
CURRENT_VERSION=$("$BIN_DIR/$BINARY_NAME" --version 2>&1 | head -1 || echo "unknown")
fi
echo_info "Current version: $CURRENT_VERSION"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Undefined echo_info() function usage.

Lines 61 and 70 call echo_info() which is not defined. The script only defines echo_step(), echo_success(), echo_warning(), and echo_error() at lines 35-38.

Add the missing function definition:

 echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; }
 echo_success() { echo -e "${GREEN}[+] $1${NC}"; }
 echo_warning() { echo -e "${YELLOW}[!] $1${NC}"; }
 echo_error() { echo -e "${RED}[-] $1${NC}"; }
+echo_info() { echo -e "${CYAN}[*] $1${NC}"; }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In scripts/update-cliproxyapi.sh around lines 58 to 70 the script calls
echo_info() but that helper is not defined (only
echo_step/echo_success/echo_warning/echo_error exist); add a new echo_info
function consistent with the other helpers (print to stdout with the same
prefix/format as the other echo_* functions) near the existing helper
definitions (around lines 35-38) so calls to echo_info succeed and formatting
remains consistent.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 issues found across 10 files

Prompt for AI agents (all 7 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="scripts/install-cliproxyapi.sh">

<violation number="1" location="scripts/install-cliproxyapi.sh:256">
P2: Shell detection logic is flawed - `$BASH_VERSION` is always set because this script runs under bash (shebang `#!/usr/bin/env bash`). This means zsh users will incorrectly have PATH added to `.bashrc` instead of `.zshrc`. Use `$SHELL` environment variable to detect the user&#39;s actual login shell instead.</violation>
</file>

<file name="scripts/AGENTS.md">

<violation number="1" location="scripts/AGENTS.md:234">
P2: This shell detection logic will always detect bash because `$BASH_VERSION` is set by the bash interpreter running the script, not by the user&#39;s login shell. Use `$SHELL` instead to correctly detect the user&#39;s shell config file.

Example fix:
```bash
case &quot;$SHELL&quot; in
    */bash) SHELL_RC=&quot;$HOME/.bashrc&quot; ;;
    */zsh) SHELL_RC=&quot;$HOME/.zshrc&quot; ;;
    *) SHELL_RC=&quot;$HOME/.profile&quot; ;;
esac
```</violation>
</file>

<file name="scripts/start-cliproxyapi.sh">

<violation number="1" location="scripts/start-cliproxyapi.sh:33">
P2: Logic bug: `is_running` check after removing PID file will always return false. The `rm -f &quot;$PID_FILE&quot;` on line 97 removes the file that `is_running()` depends on, so the subsequent check can never detect if the process failed to stop. Move the PID file removal after the final verification, or check the process directly using the stored `$PID` variable.</violation>
</file>

<file name="scripts/gui-cliproxyapi.sh">

<violation number="1" location="scripts/gui-cliproxyapi.sh:190">
P2: The GUI_PORT variable is not passed to the API server, so the `--port` option is ignored. The API server will always run on its hardcoded port 8318.</violation>
</file>

<file name="scripts/uninstall-cliproxyapi.sh">

<violation number="1" location="scripts/uninstall-cliproxyapi.sh:54">
P1: `echo_info` function is used but never defined. The script will fail at runtime because `set -e` is enabled. Add the missing function definition alongside the other echo helpers.</violation>
</file>

<file name="scripts/update-cliproxyapi.sh">

<violation number="1" location="scripts/update-cliproxyapi.sh:38">
P0: Function `echo_info` is used but never defined. This will cause the script to fail at runtime with &#39;command not found&#39;. Add the missing function definition alongside the other echo helper functions.</violation>
</file>

<file name="scripts/cliproxyapi-api-server.py">

<violation number="1" location="scripts/cliproxyapi-api-server.py:299">
P1: Server binds to all network interfaces instead of localhost only. This exposes the management API to the entire network, allowing remote control of server start/stop, OAuth, and updates. For a local management API, bind to `127.0.0.1` instead.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Ask questions if you need clarification on any suggestion

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

# Add ~/.local/bin to PATH if not already
echo_step "Configuring PATH..."
SHELL_RC=""
if [ -n "$BASH_VERSION" ]; then
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Shell detection logic is flawed - $BASH_VERSION is always set because this script runs under bash (shebang #!/usr/bin/env bash). This means zsh users will incorrectly have PATH added to .bashrc instead of .zshrc. Use $SHELL environment variable to detect the user's actual login shell instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/install-cliproxyapi.sh, line 256:

<comment>Shell detection logic is flawed - `$BASH_VERSION` is always set because this script runs under bash (shebang `#!/usr/bin/env bash`). This means zsh users will incorrectly have PATH added to `.bashrc` instead of `.zshrc`. Use `$SHELL` environment variable to detect the user&#39;s actual login shell instead.</comment>

<file context>
@@ -0,0 +1,389 @@
+# Add ~/.local/bin to PATH if not already
+echo_step &quot;Configuring PATH...&quot;
+SHELL_RC=&quot;&quot;
+if [ -n &quot;$BASH_VERSION&quot; ]; then
+    SHELL_RC=&quot;$HOME/.bashrc&quot;
+elif [ -n &quot;$ZSH_VERSION&quot; ]; then
</file context>
Fix with Cubic

Comment on lines +234 to +240
if [ -n "$BASH_VERSION" ]; then
SHELL_RC="$HOME/.bashrc"
elif [ -n "$ZSH_VERSION" ]; then
SHELL_RC="$HOME/.zshrc"
else
SHELL_RC="$HOME/.profile"
fi
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This shell detection logic will always detect bash because $BASH_VERSION is set by the bash interpreter running the script, not by the user's login shell. Use $SHELL instead to correctly detect the user's shell config file.

Example fix:

case "$SHELL" in
    */bash) SHELL_RC="$HOME/.bashrc" ;;
    */zsh) SHELL_RC="$HOME/.zshrc" ;;
    *) SHELL_RC="$HOME/.profile" ;;
esac
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/AGENTS.md, line 234:

<comment>This shell detection logic will always detect bash because `$BASH_VERSION` is set by the bash interpreter running the script, not by the user&#39;s login shell. Use `$SHELL` instead to correctly detect the user&#39;s shell config file.

Example fix:
```bash
case &quot;$SHELL&quot; in
    */bash) SHELL_RC=&quot;$HOME/.bashrc&quot; ;;
    */zsh) SHELL_RC=&quot;$HOME/.zshrc&quot; ;;
    *) SHELL_RC=&quot;$HOME/.profile&quot; ;;
esac
```</comment>

<file context>
@@ -117,8 +143,118 @@ Select-String -Path &quot;scripts\NEW_SCRIPT.ps1&quot; -Pattern &quot;ErrorActionPreference&quot;  #
+esac
+
+# Detect shell config file
+if [ -n &quot;$BASH_VERSION&quot; ]; then
+    SHELL_RC=&quot;$HOME/.bashrc&quot;
+elif [ -n &quot;$ZSH_VERSION&quot; ]; then
</file context>
Suggested change
if [ -n "$BASH_VERSION" ]; then
SHELL_RC="$HOME/.bashrc"
elif [ -n "$ZSH_VERSION" ]; then
SHELL_RC="$HOME/.zshrc"
else
SHELL_RC="$HOME/.profile"
fi
case "$SHELL" in
*/bash) SHELL_RC="$HOME/.bashrc" ;;
*/zsh) SHELL_RC="$HOME/.zshrc" ;;
*) SHELL_RC="$HOME/.profile" ;;
esac
Fix with Cubic

if ps -p "$PID" > /dev/null 2>&1; then
return 0
else
rm -f "$PID_FILE"
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Logic bug: is_running check after removing PID file will always return false. The rm -f "$PID_FILE" on line 97 removes the file that is_running() depends on, so the subsequent check can never detect if the process failed to stop. Move the PID file removal after the final verification, or check the process directly using the stored $PID variable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/start-cliproxyapi.sh, line 33:

<comment>Logic bug: `is_running` check after removing PID file will always return false. The `rm -f &quot;$PID_FILE&quot;` on line 97 removes the file that `is_running()` depends on, so the subsequent check can never detect if the process failed to stop. Move the PID file removal after the final verification, or check the process directly using the stored `$PID` variable.</comment>

<file context>
@@ -0,0 +1,207 @@
+        if ps -p &quot;$PID&quot; &gt; /dev/null 2&gt;&amp;1; then
+            return 0
+        else
+            rm -f &quot;$PID_FILE&quot;
+            return 1
+        fi
</file context>
Fix with Cubic

if command -v python3 &> /dev/null; then
if [ -f "$API_SERVER" ]; then
# Use Python API server with management endpoints
python3 "$API_SERVER" 2>/dev/null
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The GUI_PORT variable is not passed to the API server, so the --port option is ignored. The API server will always run on its hardcoded port 8318.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/gui-cliproxyapi.sh, line 190:

<comment>The GUI_PORT variable is not passed to the API server, so the `--port` option is ignored. The API server will always run on its hardcoded port 8318.</comment>

<file context>
@@ -0,0 +1,203 @@
+if command -v python3 &amp;&gt; /dev/null; then
+    if [ -f &quot;$API_SERVER&quot; ]; then
+        # Use Python API server with management endpoints
+        python3 &quot;$API_SERVER&quot; 2&gt;/dev/null
+    else
+        # Fallback to simple HTTP server
</file context>
Fix with Cubic

echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; }
echo_success() { echo -e "${GREEN}[+] $1${NC}"; }
echo_warning() { echo -e "${YELLOW}[!] $1${NC}"; }
echo_error() { echo -e "${RED}[-] $1${NC}"; }
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: Function echo_info is used but never defined. This will cause the script to fail at runtime with 'command not found'. Add the missing function definition alongside the other echo helper functions.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/update-cliproxyapi.sh, line 38:

<comment>Function `echo_info` is used but never defined. This will cause the script to fail at runtime with &#39;command not found&#39;. Add the missing function definition alongside the other echo helper functions.</comment>

<file context>
@@ -0,0 +1,234 @@
+echo_step() { echo -e &quot;\n${CYAN}[*] $1${NC}&quot;; }
+echo_success() { echo -e &quot;${GREEN}[+] $1${NC}&quot;; }
+echo_warning() { echo -e &quot;${YELLOW}[!] $1${NC}&quot;; }
+echo_error() { echo -e &quot;${RED}[-] $1${NC}&quot;; }
+
+echo -e &quot;${MAGENTA}&quot;
</file context>
Fix with Cubic

if gui_dir.exists():
os.chdir(gui_dir)

with socketserver.TCPServer(("", PORT), APIHandler) as httpd:
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Server binds to all network interfaces instead of localhost only. This exposes the management API to the entire network, allowing remote control of server start/stop, OAuth, and updates. For a local management API, bind to 127.0.0.1 instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/cliproxyapi-api-server.py, line 299:

<comment>Server binds to all network interfaces instead of localhost only. This exposes the management API to the entire network, allowing remote control of server start/stop, OAuth, and updates. For a local management API, bind to `127.0.0.1` instead.</comment>

<file context>
@@ -0,0 +1,309 @@
+    if gui_dir.exists():
+        os.chdir(gui_dir)
+
+    with socketserver.TCPServer((&quot;&quot;, PORT), APIHandler) as httpd:
+        print(f&quot;API Server running on http://localhost:{PORT}&quot;)
+        print(f&quot;GUI available at http://localhost:{PORT}&quot;)
</file context>
Fix with Cubic

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive Linux and macOS support to CLIProxyAPIPlus-Easy-Installation, transforming it from a Windows-only toolkit into a truly cross-platform solution. The implementation includes six bash scripts for installation, server management, OAuth authentication, updates, GUI control, and uninstallation, along with a Python REST API backend for the GUI and updated documentation in both English and Indonesian.

Key Changes

  • Bash installation and management scripts for Linux/macOS with auto-detection of OS and architecture, supporting both prebuilt binaries and source builds
  • Python REST API server to power the GUI Control Center with 12+ management endpoints
  • Comprehensive documentation updates including installation guides, testing instructions, and bash scripting conventions in AGENTS.md

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
scripts/install-cliproxyapi.sh Main installer with OS/architecture detection, binary installation, and configuration setup
scripts/start-cliproxyapi.sh Server lifecycle manager with PID-based tracking and background/foreground modes
scripts/update-cliproxyapi.sh Update script supporting prebuilt binaries and source builds with automatic server restart
scripts/uninstall-cliproxyapi.sh Clean removal script with optional preservation of auth tokens
scripts/gui-cliproxyapi.sh GUI launcher with browser auto-open and fallback to minimal HTML interface
scripts/cliproxyapi-oauth.sh Interactive OAuth helper for 8 providers with batch login support
scripts/cliproxyapi-api-server.py Python REST API backend providing management endpoints for the GUI
scripts/AGENTS.md Enhanced documentation with bash patterns, conventions, and best practices
README.md Added Linux/macOS quick start section with installation and testing instructions
README_ID.md Indonesian translation of Linux/macOS documentation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

MAGENTA='\033[0;35m'
NC='\033[0m' # No Color

echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; }
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent naming for output functions across bash scripts. This script defines echo_step() while all other scripts in this PR (start-cliproxyapi.sh, update-cliproxyapi.sh, cliproxyapi-oauth.sh, gui-cliproxyapi.sh, uninstall-cliproxyapi.sh) use echo_info() for informational output.

For consistency, rename echo_step() to echo_info() throughout this script. This will establish a consistent convention across all bash scripts.

Copilot uses AI. Check for mistakes.
echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; }
echo_success() { echo -e "${GREEN}[+] $1${NC}"; }
echo_warning() { echo -e "${YELLOW}[!] $1${NC}"; }
echo_error() { echo -e "${RED}[-] $1${NC}"; }
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing echo_info() function definition. The script calls echo_info() on lines 54, 60, 121, 122, 139, 159, 160, and 164, but only defines echo_step(), echo_success(), echo_warning(), and echo_error(). This will cause runtime errors when echo_info() is called.

Add the missing function definition:

echo_info() { echo -e "${CYAN}[*] $1${NC}"; }

Alternatively, for consistency with most other scripts in this PR, you could use echo_info() everywhere and remove echo_step(), or standardize on one naming convention across all scripts.

Suggested change
echo_error() { echo -e "${RED}[-] $1${NC}"; }
echo_error() { echo -e "${RED}[-] $1${NC}"; }
echo_info() { echo -e "${CYAN}[*] $1${NC}"; }

Copilot uses AI. Check for mistakes.
if command -v python3 &> /dev/null; then
if [ -f "$API_SERVER" ]; then
# Use Python API server with management endpoints
python3 "$API_SERVER" 2>/dev/null
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The custom port specified via --port flag is not passed to the Python API server. The cliproxyapi-api-server.py script has a hardcoded PORT = 8318 on line 15, so even if a user specifies --port 9000, the API server will still run on port 8318.

To fix this, pass the GUI_PORT as an argument or environment variable to the Python script:

GUI_PORT=$GUI_PORT python3 "$API_SERVER" 2>/dev/null

Then update the Python script to read from the environment:

PORT = int(os.environ.get('GUI_PORT', 8318))
Suggested change
python3 "$API_SERVER" 2>/dev/null
GUI_PORT=$GUI_PORT python3 "$API_SERVER" 2>/dev/null

Copilot uses AI. Check for mistakes.
Comment on lines +226 to +244
def handle_models(self):
"""Get available models"""
try:
result = subprocess.run(
["curl", "-s", "-H", "Authorization: Bearer sk-dummy",
"http://localhost:8317/v1/models"],
capture_output=True,
text=True,
timeout=5
)

if result.returncode == 0:
data = json.loads(result.stdout)
models = [m["id"] for m in data.get("data", [])]
self.send_json({"models": models})
else:
self.send_json({"models": []})
except Exception as e:
self.send_json({"models": [], "error": str(e)})
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using curl via subprocess to make HTTP requests is inefficient and requires an external dependency. Python has built-in libraries for HTTP requests that would be more reliable and portable.

Consider using Python's urllib.request (built-in) or checking if requests is available:

import urllib.request
import urllib.error

def handle_models(self):
    """Get available models"""
    try:
        req = urllib.request.Request(
            "http://localhost:8317/v1/models",
            headers={"Authorization": "Bearer sk-dummy"}
        )
        with urllib.request.urlopen(req, timeout=5) as response:
            data = json.loads(response.read())
            models = [m["id"] for m in data.get("data", [])]
            self.send_json({"models": models})
    except Exception as e:
        self.send_json({"models": [], "error": str(e)})

Copilot uses AI. Check for mistakes.
RED='\033[0;31m'
NC='\033[0m' # No Color

echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; }
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation shows echo_step() as the standard function name, but most bash scripts in this PR (start-cliproxyapi.sh, cliproxyapi-oauth.sh, gui-cliproxyapi.sh) use echo_info() instead. This inconsistency between documentation and implementation will confuse future contributors.

Update the documentation to reflect the actual convention used in most scripts:

echo_info() { echo -e "${CYAN}[*] $1${NC}"; }

Or alternatively, standardize all scripts to use echo_step() if that's the preferred naming.

Suggested change
echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; }
echo_info() { echo -e "\n${CYAN}[*] $1${NC}"; }

Copilot uses AI. Check for mistakes.
echo_info "Starting server in background..."
nohup "$BINARY_PATH" --config "$CONFIG_PATH" > "$LOG_FILE" 2>&1 &
PID=$!
echo $PID > "$PID_FILE"
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PID variable should be quoted when writing to the file for better shell script practices, although in this case it's unlikely to cause issues since PIDs don't contain spaces.

Change:

echo $PID > "$PID_FILE"

To:

echo "$PID" > "$PID_FILE"
Suggested change
echo $PID > "$PID_FILE"
echo "$PID" > "$PID_FILE"

Copilot uses AI. Check for mistakes.
Comment on lines +113 to +118
# Remove Droid custom models
if [ -f "$FACTORY_DIR/config.json" ]; then
echo_step "Removing Droid custom models..."
rm -f "$FACTORY_DIR/config.json"
echo_success "Removed: $FACTORY_DIR/config.json"
fi
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The uninstall script completely removes ~/.factory/config.json when --all flag is used. This will delete all custom model configurations that users may have set up, not just CLIProxyAPI-Plus models. If users have configured other custom models in Factory Droid, those will be lost.

Consider either:

  1. Warning the user more explicitly that this will remove ALL custom models
  2. Attempting to remove only CLIProxyAPI-Plus entries from the config.json (using jq or similar)
  3. Creating a backup before deletion

At minimum, add a more explicit warning message before this deletion:

echo_warning "This will remove ALL Factory Droid custom models, not just CLIProxyAPI-Plus models"

Copilot uses AI. Check for mistakes.
MAGENTA='\033[0;35m'
NC='\033[0m'

echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; }
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent naming for output functions across bash scripts. The update-cliproxyapi.sh script uses both echo_step() (defined on line 35) and echo_info() (called on lines 61, 70, 113, etc.) without defining echo_info(). This will cause runtime errors when the undefined function is called.

For consistency with other scripts in this PR (start-cliproxyapi.sh, cliproxyapi-oauth.sh, gui-cliproxyapi.sh, uninstall-cliproxyapi.sh which all use echo_info()), rename echo_step() to echo_info() in this script.

Suggested change
echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; }
echo_info() { echo -e "\n${CYAN}[*] $1${NC}"; }

Copilot uses AI. Check for mistakes.
import json
import subprocess
import os
from urllib.parse import urlparse, parse_qs
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'parse_qs' is not used.

Suggested change
from urllib.parse import urlparse, parse_qs
from urllib.parse import urlparse

Copilot uses AI. Check for mistakes.
Comment on lines +187 to +194
if command -v python3 &> /dev/null; then
if [ -f "$API_SERVER" ]; then
# Use Python API server with management endpoints
python3 "$API_SERVER" 2>/dev/null
else
# Fallback to simple HTTP server
cd "$GUI_DIR"
python3 -m http.server "$GUI_PORT" 2>/dev/null
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gui-cliproxyapi.sh starts the Python management server (cliproxyapi-api-server) as a long-lived HTTP API without any authentication or CSRF protection, exposing sensitive operations like start/stop/restart and config access over HTTP on port 8318. Because the backend binds to all interfaces and sets permissive CORS, any process on the local network or any web page running in the user’s browser can issue cross-origin requests to control the proxy and read configuration data. Restrict the management server to 127.0.0.1, add a per-user auth token checked on each request, and narrow CORS to the local GUI origin instead of * before starting it here.

Suggested change
if command -v python3 &> /dev/null; then
if [ -f "$API_SERVER" ]; then
# Use Python API server with management endpoints
python3 "$API_SERVER" 2>/dev/null
else
# Fallback to simple HTTP server
cd "$GUI_DIR"
python3 -m http.server "$GUI_PORT" 2>/dev/null
# Generate a random per-user auth token
AUTH_TOKEN="$(head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 32)"
CORS_ORIGIN="http://localhost:$GUI_PORT"
if command -v python3 &> /dev/null; then
if [ -f "$API_SERVER" ]; then
# Use Python API server with management endpoints
python3 "$API_SERVER" \
--host 127.0.0.1 \
--port "$GUI_PORT" \
--auth-token "$AUTH_TOKEN" \
--cors-origin "$CORS_ORIGIN" \
2>/dev/null
else
# Fallback to simple HTTP server
cd "$GUI_DIR"
python3 -m http.server "$GUI_PORT" --bind 127.0.0.1 2>/dev/null

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant